/**
 * @brief 一个简单的命令行交互模式演示
 *
 * 演示功能：
 * 1. 通过配置文件创建API实例；
 * 2. 输入命令启动API；
 * 3. 输入命令停止API；
 * 4. 输入命令登录柜台；
 * 5. 输入命令登出柜台；
 * 6. 输入命令发送默认报单；
 * 7. 输入命令撤销所有已有的报单；
 */

#include <iostream>
#include <map>
#include "ExampleTrader.h"

class Example_06_Trader : public ExampleTrader {
public:
    Example_06_Trader() = default;

    ~Example_06_Trader() override {
        // release api.
        if (mApi) {
            mApi->stop();
            delete mApi;
            mApi = nullptr;
        }
    };

    void onStart(int errorCode, bool isFirstTime) override {
        ExampleTrader::onStart(errorCode, isFirstTime);

        printf("api start ok.\n");
    }

    void onStop(int reason) override {
        ExampleTrader::onStop(reason);
        printf("api stopped, reason: %d.\n", reason);
    }

    void onServerReboot() override {
        printf("trade system reboot, clear local data.\n");
        mInstrument = nullptr;
        mOrders.clear();
    }

    void onLogin(int errorCode, int exchangeCount) override {
        ExampleTrader::onLogin(errorCode, exchangeCount);

        if (errorCode != 0)
            printf("login failed, error code: %d.\n", errorCode);
        else
            printf("login success, exchange count=%d.\n", exchangeCount);
    }

    void onLogout(int errorCode) override {
        ExampleTrader::onLogout(errorCode);

        printf("logout success.\n");
    }

    void onReadyForTrading(const XTFAccount *account) override {
        ExampleTrader::onReadyForTrading(account);

        printf("ready for trading.\n");
        mInstrument = mApi->getInstrumentByID(mInstrumentID.c_str()); // 查询可用的合约对象
        if (!mInstrument) {
            printf("instrument not found: %s\n", mInstrumentID.c_str());
            exit(0);
        }

        mOrderLocalId = account->lastLocalOrderID;
    }

    void onLoadFinished(const XTFAccount *account) override {
        ExampleTrader::onLoadFinished(account);

        printf("load data finished.\n");
    }

    void onAccount(int event, int action, const XTFAccount *account) override {
        // 账户信息发生变化时回调该接口，如：出入金变化
        if (event == XTF_EVT_AccountCashInOut) {
            if (action == XTF_CASH_In) printf("cash in.\n");
            if (action == XTF_CASH_Out) printf("cash out.\n");
        } else {
            printf("account is changed: event=%d, action=%d, accountID=%s\n",
                   event, action, account->accountID);
        }
    }

    void onExchange(int event, int channelID, const XTFExchange *exchange) override {
        // 交易所信息发生变化时回调该接口，如：交易所前置变化
        printf("exchange is changed: event=%d, channelID=%d, exchange=%s\n",
                event, channelID, exchange->exchangeID);
    }

    void onInstrument(int event, const XTFInstrument *instrument) override {
        // 合约属性发生变化时回调该接口，如：状态变化
        if (event == XTF_EVT_InstrumentStatusChanged) {
            printf("instrument status changed: %s %d.\n",
                   instrument->instrumentID, instrument->status);
        }
    }

    void onChangePassword(int errorCode) override {
        printf("password is changed: errorCode=%d\n", errorCode);
    }

    void onOrder(int errorCode, const XTFOrder *order) override {
        printf("recv order report: action=%d, sys-id=%d, "
               "status=%s, error-code=%d.\n",
               order->actionType, order->sysOrderID,
               getOrderStatus(order->orderStatus), errorCode);
        if (errorCode == 0) {
            if (order->orderStatus == XTF_OS_Queuing) {
                mOrders[order->sysOrderID] = order;
            }
        }
    }

    void onCancelOrder(int errorCode, const XTFOrder *cancelOrder) override {
        printf("recv cancel order report: sys-id=%d, status=%s, error-code=%d.\n",
               cancelOrder->sysOrderID, getOrderStatus(cancelOrder->orderStatus), errorCode);
        if (errorCode == 0 || errorCode == 1198) {
            auto iter = mOrders.find(cancelOrder->sysOrderID);
            if (iter != mOrders.end())
                mOrders.erase(iter);
        }
    }

    void onTrade(const XTFTrade *trade) override {
        printf("recv trade report: trade-id=%ld, price=%.4f, volume=%d/%d, sys-order-id=%d\n",
                trade->tradeID, trade->tradePrice, trade->order->totalTradedVolume,
                trade->order->orderVolume, trade->order->sysOrderID);
    }

    void onEvent(const XTFEvent &event) override {
        printf("recv event: %d.\n", event.eventID);
    }

    void onError(int errorCode, void *data, size_t size) override {
        printf("something is wrong, error code: %d.\n", errorCode);
    }

    void start() {
        if (mApi) {
            printf("error: trader has been started.\n");
            return;
        }

        mOrderLocalId = 0;
        mApi = makeXTFApi(mConfigPath.c_str());
        if (mApi == nullptr) {
            printf("error: create xtf api failed, please check config: %s.\n", mConfigPath.c_str());
            exit(0);
        }

        printf("api starting..., config: %s.\n", mConfigPath.c_str());
        int ret = mApi->start(this);
        if (ret != 0) {
            printf("start failed, error code: %d\n", ret);
            exit(0);
        }
    }

    void stop() {
        if (!mApi) {
            printf("error: trader is not started.\n");
            return;
        }

        printf("api stopping...\n");
        int ret = mApi->stop();
        if (ret == 0) {
            // API停止操作是异步操作，需要等待一定时间，以防API对象回调时失效。
            // 4.1.664及更高版本不存在此问题，不需要增加延时。
            usleep(100000);

            delete mApi;
            mApi = nullptr;
        } else {
            printf("api stop failed, error code: %d\n", ret);
        }
    }

    void login() {
        if (!mApi) return;
        printf("api logging in...\n");
        int ret = mApi->login();
        if (ret != 0) {
            printf("api logging in failed, error code: %d\n", ret);
        }
    }

    void logout() {
        if (!mApi) return;
        printf("api logging out...\n");
        int ret = mApi->logout();
        if (ret != 0) {
            printf("api logging out failed, error code: %d\n", ret);
        }
    }

    void insertOrder() {
        if (!mApi) {
            printf("api is not started.\n");
            return;
        }

        if (!mInstrument) {
            printf("instrument is not found: %s\n", mInstrumentID.c_str());
            return;
        }

        printf("api prepare order...\n");
        XTFInputOrder order{};
        order.localOrderID = ++mOrderLocalId; // 建议使用本地唯一的编号
        order.direction = XTF_D_Buy; // or XTF_D_Sell
        order.offsetFlag = XTF_OF_Open;
        order.orderType = XTF_ODT_Limit;
        order.price = mPrice;
        order.volume = mVolume;
        order.channelSelectionType = XTF_CS_Auto;
        order.channelID = 0;
        order.orderFlag = XTF_ODF_Normal;
        order.instrument = mInstrument;

        printf("api insert order...\n");
        int ret = mApi->insertOrder(order);
        if (ret != 0) {
            printf("api insert order failed, error code: %d\n", ret);
        }
    }

    void cancelOrder() {
        if (!mApi) {
            printf("api is not started.\n");
            return;
        }

        if (mOrders.empty()) {
            printf("no orders need cancel.\n");
            return;
        }

        printf("api cancel order...\n");
        for (auto &iter: mOrders) {
            printf("cancel order: sys-id=%d.\n", iter.first);
            int ret = mApi->cancelOrder(iter.second);
            if (ret != 0) {
                printf("api cancel order failed, error code: %d\n", ret);
            }
            usleep(500000); // sleep 500ms;
        }
    }

private:
    const XTFInstrument *mInstrument;
    std::map<int, const XTFOrder *> mOrders;
    int mOrderLocalId;
    int mStatus = 0;
};


/**
 * @brief 一个简单的报撤单功能演示，API登录柜台后，报一手多头开仓单，等待3秒后，尝试撤单。
 *
 * @param config
 * @param instrumentId
 */
void runExample(const std::string &configPath, const std::string &instrumentId, double price, int volume) {
    printf("start example 06.\n");

    Example_06_Trader trader;
    trader.setConfigPath(configPath);
    trader.setInstrumentID(instrumentId);
    trader.setPrice(price);
    trader.setVolume(volume);

    printf("api version: %s\n", getXTFVersion());
    printf("input 'quit' or 'exit' to exit.\n");
    std::string cmdline;
    std::string prefix = "> ";
    while (true) {
        std::cout << prefix;
        std::getline(std::cin, cmdline);
        if (cmdline.empty()) continue;
        if (cmdline == "start") {
            if (trader.isStarted()) {
                printf("trader is started.\n");
            } else {
                trader.start();
            }
        }
        if (cmdline == "stop") {
            if (trader.isStopped()) {
                printf(".\n");
            } else {
                trader.stop();
            }
        }
        if (cmdline == "login") {
            if (!trader.isLoggedIn()) {
                printf("trader is logged in.\n");
            } else {
                trader.login();
            }
        }
        if (cmdline == "logout") {
            if (!trader.isLoggedOut()) {
                printf("trader is logged out.\n");
            } else {
                trader.logout();
            }
        }
        if (cmdline == "insert") {
            if (!trader.isLoadFinished()) {
                printf("wait for data load finished.\n");
            } else {
                trader.insertOrder();
            }
        }
        if (cmdline == "cancel") {
            if (!trader.isLoadFinished()) {
                printf("wait for data load finished.\n");
            } else {
                trader.cancelOrder();
            }
        }
        if (cmdline == "exit" || cmdline == "quit") {
            break;
        } else {
            printf("start     start trader.\n");
            printf("stop      stop trader.\n");
            printf("login     login trader.\n");
            printf("logout    logout trader.\n");
            printf("insert    insert default order.\n");
            printf("cancel    cancel existed orders.\n");
        }
    }
}

int main(int argc, const char *argv[]) {
    // TODO: 解析传入参数，提取相关的配置
    std::string configPath = "../config/xtf_trader_api.config";
    std::string instrumentId = "au2212";
    double price = 301.50f;
    int volume = 1;
    runExample(configPath, instrumentId, price, volume);
    return 0;
}
